﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Primitives;
using StarUtils.Extension;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace StarUtils.Http
{
    public static class WebHelper
    {
        private static string _ip;

        public static IHttpContextAccessor HttpContextAccessor { get; set; }

        public static HttpContext HttpContext => HttpContextAccessor?.HttpContext;

        public static IHostingEnvironment Environment { get; set; }

        public static Microsoft.AspNetCore.Http.HttpRequest Request => HttpContext?.Request;

        public static HttpResponse Response => HttpContext?.Response;

        public static string LocalIpAddress
        {
            get
            {
                try
                {
                    IPAddress localIpAddress = HttpContext.Connection.LocalIpAddress;
                    return IPAddress.IsLoopback(localIpAddress) ? IPAddress.Loopback.ToString() : localIpAddress.MapToIPv4().ToString();
                }
                catch
                {
                    return IPAddress.Loopback.ToString();
                }
            }
        }

        public static string RequestType => HttpContext?.Request?.Method;

        public static IFormCollection Form => HttpContext?.Request?.Form;

        public static string Body
        {
            get
            {
                using StreamReader streamReader = new StreamReader(Request.Body);
                return streamReader.ReadToEnd();
            }
        }

        public static string Url => HttpContext?.Request?.GetDisplayUrl();

        public static string IP
        {
            get
            {
                if (!string.IsNullOrWhiteSpace(_ip))
                {
                    return _ip;
                }

                string text = GetNinxIp();
                if (!string.IsNullOrWhiteSpace(text))
                {
                    return text;
                }

                string[] source = new string[2] { "127.0.0.1", "::1" };
                if (string.IsNullOrWhiteSpace(text) || source.Contains(text))
                {
                    text = Sys.IsWindows ? GetLanIP() : GetLanIP(NetworkInterfaceType.Ethernet);
                }

                return text;
            }
        }

        public static string Host
        {
            get
            {
                if (HttpContext != null)
                {
                    return GetClientHostName();
                }

                return Dns.GetHostName();
            }
        }

        public static string Browser
        {
            get
            {
                StringValues? stringValues = HttpContext?.Request?.Headers["User-Agent"];
                if (!stringValues.HasValue)
                {
                    return null;
                }

                return stringValues.GetValueOrDefault();
            }
        }

        public static string RootPath => Environment?.ContentRootPath;

        public static string WebRootPath => Environment?.WebRootPath;

        public static string ContentType => HttpContext?.Request?.ContentType;

        public static string QueryString => HttpContext?.Request?.QueryString.ToString();

        public static bool IsAjax()
        {
            StringValues value = Request.Headers.FirstOrDefault((KeyValuePair<string, StringValues> p) => p.Key.ToLower() == "x-requested-with")!.Value;
            StringValues value2 = Request.Headers.FirstOrDefault((KeyValuePair<string, StringValues> p) => p.Key.ToLower() == "content-type")!.Value;
            if (!value.Equals("XMLHttpRequest"))
            {
                return value2.Equals("application/json");
            }

            return true;
        }

        public static void SetIp(string ip)
        {
            _ip = ip;
        }

        public static void ResetIp()
        {
            _ip = null;
        }

        private static string GetLanIP()
        {
            IPAddress[] hostAddresses = Dns.GetHostAddresses(Dns.GetHostName());
            foreach (IPAddress iPAddress in hostAddresses)
            {
                if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
                {
                    return iPAddress.MapToIPv4().ToString();
                }
            }

            return string.Empty;
        }

        private static string GetLanIP(NetworkInterfaceType type)
        {
            try
            {
                NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
                foreach (NetworkInterface networkInterface in allNetworkInterfaces)
                {
                    if (networkInterface.NetworkInterfaceType != type || networkInterface.OperationalStatus != OperationalStatus.Up)
                    {
                        continue;
                    }

                    IPInterfaceProperties iPProperties = networkInterface.GetIPProperties();
                    if (iPProperties.GatewayAddresses.FirstOrDefault() == null)
                    {
                        continue;
                    }

                    foreach (UnicastIPAddressInformation unicastAddress in iPProperties.UnicastAddresses)
                    {
                        if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            return unicastAddress.Address.ToString();
                        }
                    }
                }
            }
            catch
            {
                return string.Empty;
            }

            return string.Empty;
        }

        private static string GetNinxIp()
        {
            string result = string.Empty;
            if (HttpContext.Request.Headers.ContainsKey("X-Real-IP"))
            {
                result = HttpContext.Request.Headers["X-Real-IP"].ToString();
            }
            else if (HttpContext.Request.Headers.ContainsKey("X-Forwarded-For"))
            {
                result = HttpContext.Request.Headers["X-Forwarded-For"].ToString();
            }

            return result;
        }

        private static string GetClientHostName()
        {
            string remoteAddress = GetRemoteAddress();
            if (string.IsNullOrWhiteSpace(remoteAddress))
            {
                return Dns.GetHostName();
            }

            string hostName = Dns.GetHostEntry(IPAddress.Parse(remoteAddress)).HostName;
            if (hostName == "localhost.localdomain")
            {
                hostName = Dns.GetHostName();
            }

            return hostName;
        }

        private static string GetRemoteAddress()
        {
            StringValues? stringValues = HttpContext?.Request?.Headers["HTTP_X_FORWARDED_FOR"] ?? HttpContext?.Request?.Headers["REMOTE_ADDR"];
            if (!stringValues.HasValue)
            {
                return null;
            }

            return stringValues.GetValueOrDefault();
        }

        static WebHelper()
        {
            ServicePointManager.DefaultConnectionLimit = 200;
        }

        public static WebClient Client(HttpClient client = null)
        {
            return new WebClient(client);
        }

        public static WebClient<TResult> Client<TResult>(HttpClient client = null) where TResult : class
        {
            return new WebClient<TResult>(client);
        }

        public static List<IFormFile> GetFiles()
        {
            List<IFormFile> list = new List<IFormFile>();
            IFormFileCollection files = HttpContext.Request.Form.Files;
            if (files == null || files.Count == 0)
            {
                return list;
            }

            list.AddRange(files.Where((file) => file != null && file.Length > 0));
            return list;
        }

        public static IFormFile GetFile()
        {
            List<IFormFile> files = GetFiles();
            if (files.Count != 0)
            {
                return files[0];
            }

            return null;
        }

        public static string UrlEncode(string url, bool isUpper = false)
        {
            return UrlEncode(url, Encoding.UTF8, isUpper);
        }

        public static string UrlEncode(string url, string encoding, bool isUpper = false)
        {
            encoding = string.IsNullOrWhiteSpace(encoding) ? "UTF-8" : encoding;
            return UrlEncode(url, Encoding.GetEncoding(encoding), isUpper);
        }

        public static string UrlEncode(string url, Encoding encoding, bool isUpper = false)
        {
            string text = HttpUtility.UrlEncode(url, encoding);
            if (!isUpper)
            {
                return text;
            }

            return GetUpperEncode(text);
        }

        private static string GetUpperEncode(string encode)
        {
            StringBuilder stringBuilder = new StringBuilder();
            int num = int.MinValue;
            for (int i = 0; i < encode.Length; i++)
            {
                string text = encode[i].ToString();
                if (text == "%")
                {
                    num = i;
                }

                if (i - num == 1 || i - num == 2)
                {
                    text = text.ToUpper();
                }

                stringBuilder.Append(text);
            }

            return stringBuilder.ToString();
        }

        public static string UrlDecode(string url)
        {
            return HttpUtility.UrlDecode(url);
        }

        public static string UrlDecode(string url, Encoding encoding)
        {
            return HttpUtility.UrlDecode(url, encoding);
        }

        public static void Redirect(string url)
        {
            Response?.Redirect(url);
        }

        public static void Write(string text)
        {
            Response.ContentType = "text/plain;charset=utf-8";
            Task.Run(async delegate
            {
                await Response.WriteAsync(text);
            }).GetAwaiter().GetResult();
        }

        public static void Write(FileStream stream)
        {
            long size = stream.Length;
            byte[] buffer = new byte[size];
            stream.Read(buffer, 0, (int)size);
            stream.Dispose();
            File.Delete(stream.Name);
            Response.ContentType = "application/octet-stream";
            Response.Headers.Add("Content-Disposition", "attachment;filename=" + WebUtility.UrlEncode(Path.GetFileName(stream.Name)));
            Response.Headers.Add("Content-Length", size.ToString());
            Task.Run(async delegate
            {
                await Response.Body.WriteAsync(buffer, 0, (int)size);
            }).GetAwaiter().GetResult();
            Response.Body.Close();
        }
    }
}
